package com.huan.es8.aggregations.bucket;

import co.elastic.clients.elasticsearch._types.mapping.KeywordProperty;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.mapping.TextProperty;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import com.huan.es8.AbstractEs8Api;
import org.junit.jupiter.api.*;

import java.io.IOException;
import java.util.Arrays;

/**
 * 1. 全局聚合（不受查询条件的限制）
 * 2. 过滤聚合（对查询后的结果再次聚合）
 * 3. 基数聚合，类似SQL count(distinct(字段))，近似值
 *
 * @author huan.fu
 * @date 2022/12/10 - 10:26
 * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-global-aggregation.html">官方文档</a>
 * @see <a href="https://www.pipiho.com/es/7.7/cn/search-aggregations-metrics-cardinality-aggregation.html">官方文档</a>
 * @see <a href="https://blog.csdn.net/fu_huo_1993/article/details/128262529">博客地址</a>
 */
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class GlobalAndFiltersAggs extends AbstractEs8Api {

    @BeforeAll
    public void createIndex() throws IOException {
        client.indices()
                .create(indexRequest ->
                        indexRequest.index("index_api_log")
                                .mappings(mappings ->
                                        mappings.properties("id", new Property(new KeywordProperty.Builder().build()))
                                                .properties("message", new Property(new TextProperty.Builder().build()))
                                )
                );
        bulk("index_api_log", Arrays.asList(
                "{\"message\": \"this is info message-01\",\"id\":1}",
                "{\"message\": \"this is info message-02\",\"id\":2}",
                "{\"message\": \"this is warn message-01\",\"id\":3}",
                "{\"message\": \"this is error message\",\"id\":4}",
                "{\"message\": \"this is info and warn message\",\"id\":5}"
        ));
    }

    @Test
    @DisplayName("global and filters and cardinality 聚合")
    public void test01() throws IOException {
        SearchRequest request = SearchRequest.of(searchRequest ->
                searchRequest.index("index_api_log")
                        // 查询 message 中存在 info 和 warn 的日志
                        .query(query -> query.bool(bool -> bool.must(must -> must.match(match -> match.field("message").query("info warn")))))
                        // 查询的结果不返回
                        .size(0)
                        // 第一个聚合
                        .aggregations("agg_01", agg ->
                                agg.filters(filters ->
                                        filters.filters(f ->
                                                        f.array(
                                                                Arrays.asList(
                                                                        // 在上一步query的结果中，将 message中包含info的进行聚合
                                                                        Query.of(q -> q.match(m -> m.field("message").query("info"))),
                                                                        // 在上一步query的结果中，将 message中包含warn的进行聚合
                                                                        Query.of(q -> q.match(m -> m.field("message").query("warn")))
                                                                )
                                                        )
                                                )
                                                // 如果上一步的查询中，存在非 info 和 warn的则是否聚合到 other 桶中
                                                .otherBucket(true)
                                                // 给 other 桶取一个名字
                                                .otherBucketKey("other")
                                )
                        )
                        // 第二个聚合
                        .aggregations("agg_02", agg ->

                                agg
                                        // 此处的 global 聚合只能放在顶部
                                        .global(global -> global)
                                        // 子聚合，数据来源于所有的文档，不受上一步query结果的限制
                                        .aggregations("total", subAgg ->
                                                // 类似于SQL中的 count(distinct(字段))，是一个近似统计
                                                subAgg.cardinality(cardinality ->
                                                        // 统计的字段
                                                        cardinality.field("id")
                                                                // 精度，默认值是30000，最大值也是40000，不超过这个值的聚合近似准确值
                                                                .precisionThreshold(30000)
                                                )
                                        )
                        )
        );
        System.out.println("request: " + request);
        SearchResponse<String> response = client.search(request, String.class);
        System.out.println("response: " + response);
    }

    @AfterAll
    public void deleteIndex() throws IOException {
        deleteIndex("index_api_log");
    }
}
